package com.im;

import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.text.TextUtils;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;

// 客户端
class Client extends Socket implements Runnable {
    /**
     * 发送数据的线程。
     */
    private class SendThread extends Thread {
        private final ImProtocol mData; // 要发送的数据

        /**
         * 构造方法。
         * @param data  要发送的数据
         */
        public SendThread(ImProtocol data) {
            mData = data;
        }

        /**
         * 线程方法。
         */
        @Override
        public void run() {
            try {
                mSender.writeObject(mData);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private final Handler mCallback;    // 回调
    private int mFriendCount;           // 好友数
    private ObjectOutputStream mSender; // 数据发送者
    private static int sIconIndex;      // 头像索引
    private static String sUsername;    // 用户名

    /**
     * 构造方法。
     * @param callback  回调
     */
    public Client(Callback callback) {
        // 初始化成员
        mCallback = new Handler(callback);
        mFriendCount = 0;
        
        // 开启接收数据的线程
        new Thread(this).start();
    }

    /**
     * 添加好友。
     * @param friendName 好友名字
     * @return 结果
     */
    public ResultCode addFriend(String friendName) {
        // 检查是否超过好友上限
        if (mFriendCount > Const.MAX_USER_COUNT) {
            return ResultCode.TOO_MANY_FRIENDS;
        }

        // 检查好友名
        if (friendName.equals(sUsername)) {
            return ResultCode.FRIEND_NAME_EQUAL_SELF_NAME;
        }

        // 发送添加好友请求
        final AddFriendRequest request = new AddFriendRequest(friendName, sIconIndex, sUsername);
        this.sendData(request);

        return ResultCode.SUCCEEDED;
    }

    /**
     * 允许或拒绝添加好友。
     * @param allow     允许则为 true，否则为 false
     * @param request   添加好友请求
     */
    public void allowAddFriend(boolean allow, AddFriendRequest request) {
        // 发送结果
        AddFriendResult addFriendResult = new AddFriendResult(sIconIndex, request.mDstUsername,
            allow ? ResultCode.ADD_FRIEND_ALLOW: ResultCode.ADD_FRIEND_NOT_ALLOW, request.mSrcUsername);
        this.sendData(addFriendResult);

        // 如果允许，则好友数加1
        if (allow) {
            mFriendCount++;
        }
    }

    /**
     * 聊天。
     * @param chatInfo  聊天信息
     */
    public void chat(ChatInfo chatInfo) {
        this.sendData(chatInfo);
    }

    /**
     * 关闭客户端。
     */
    public void close() {
        // 离线
        this.logoff();

        try {
            // 延迟一下，防止数据还没发送完成就关闭网络
            Thread.sleep(100);

            // 关闭
            if (mSender != null) {
                mSender.close();
            }
            super.shutdownInput();
            super.shutdownOutput();
            super.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取好友数。
     * @return 好友数
     */
    public int getFriendCount() {
        return mFriendCount;
    }

    /**
     * 获取自己的头像索引。
     * @return 自己的头像索引
     */
    public static int getIconIndex() {
        return sIconIndex;
    }

    /**
     * 获取自己的名字。
     * @return 自己的名字
     */
    public static String getUsername() {
        return sUsername;
    }

    /**
     * 处理添加好友请求。
     * @param request    添加好友请求
     */
    private void handleAddFriendRequest(AddFriendRequest request) {
        final Message message = Message.obtain();
        message.what = Const.UM_GET_ADD_FRIEND_REQUEST;
        message.obj = request;
        mCallback.sendMessage(message);
    }

    /**
     * 处理添加好友结果。
     * @param result    添加好友结果
     */
    private void handleAddFriendResult(AddFriendResult result) {
        if (result.mResultCode == ResultCode.ADD_FRIEND_ALLOW && result.mDstIconIndex != -1) {
            mFriendCount++;
        }

        final Message message = Message.obtain();
        message.what = Const.UM_GET_ADD_FRIEND_RESULT;
        message.obj = result;
        mCallback.sendMessage(message);
    }

    /**
     * 处理聊天。
     * @param chatInfo  聊天信息
     */
    private void handleChat(ChatInfo chatInfo) {
        final Message message = Message.obtain();
        message.what = Const.UM_CHAT_S2C;
        message.obj = chatInfo;
        mCallback.sendMessage(message);
    }

    /**
     * 处理其他用户的离线请求。
     * @param request   其他用户的离线请求
     */
    private void handleFriendLogoff(LogoffRequest request) {
        final Message message = Message.obtain();
        message.what = Const.UM_FRIEND_LOGOFF;
        message.obj = request;
        mCallback.sendMessage(message);
    }

    /**
     * 处理其他用户的登录消息。
     * @param friendLogon   其他用户的登录消息
     */
    private void handleFriendLogon(FriendLogon friendLogon) {
        final Message message = Message.obtain();
        message.what = Const.UM_FRIEND_LOGON;
        message.obj = friendLogon;
        mCallback.sendMessage(message);
    }

    /**
     * 处理登录结果。
     * @param result    登录结果
     */
    private void handleLogonResult(LogonResult result) {
        final Message message = Message.obtain();
        message.what = Const.UM_GET_LOGON_RESULT;
        message.obj = result;
        mCallback.sendMessage(message);

        if (result.mResultCode == ResultCode.LOGON_SUCCEEDED) {
            sIconIndex = result.mIconIndex;
            mFriendCount = result.mFriendCount;
        } else {
            sUsername = "";
        }
    }

    /**
     * 处理注册结果。
     * @param result    注册结果
     */
    private void handleRegResult(RegResult result) {
        if (result.mResultCode != ResultCode.REG_SUCCEEDED) {
            sUsername = "";
        }

        final Message message = Message.obtain();
        message.what = Const.UM_GET_REG_RESULT;
        message.obj = result;
        mCallback.sendMessage(message);
    }

    /**
     * 离线。
     */
    public void logoff() {
        if (TextUtils.isEmpty(sUsername)) {
            return;
        }

        final LogoffRequest request = new LogoffRequest(sUsername);
        final Thread thread = this.sendData(request);
        try {
            thread.join();
        } catch (Exception e) {
            e.printStackTrace();
        }

        sUsername = "";
    }

    /**
     * 登录。
     * @param username  用户名
     * @param password  密码
     */
    public void logon(String username, String password) {
        // 发送登录请求
        final LogonRequest request = new LogonRequest(password, username);
        this.sendData(request);

        // 保存用户名
        sUsername = username;
    }

    /**
     * 处理服务器发送来的数据。
     * @param pro    协议
     */
    private void onGetMessageFromServer(ImProtocol pro) {
        switch (pro.mType) {
            case ADD_FRIEND_REQUEST:
                this.handleAddFriendRequest((AddFriendRequest)pro);
                break;
            case ADD_FRIEND_RESULT:
                this.handleAddFriendResult((AddFriendResult)pro);
                break;
            case CHAT:
                this.handleChat((ChatInfo)pro);
                break;
            case FRIEND_LOGON:
                this.handleFriendLogon((FriendLogon)pro);
                break;
            case LOGOFF:
                this.handleFriendLogoff((LogoffRequest)pro);
                break;
            case LOGON:
                this.handleLogonResult((LogonResult)pro);
                break;
            case REG:
                this.handleRegResult((RegResult)pro);
                break;
        }
    }

    /**
     * 注册。
     * @param username  用户名
     * @param password  密码
     * @param iconIndex 头像索引
     */
    public void reg(String username, String password, int iconIndex) {
        // 发送注册请求
        final RegRequest request = new RegRequest(iconIndex, password, username);
        this.sendData(request);

        // 保存用户名和头像索引
        sIconIndex = iconIndex;
        sUsername = username;
    }

    /**
     * 接收数据的线程方法。
     */
    @Override
    public void run() {
        ObjectInputStream receiver; // 数据接收者

        try {
            // 连接服务器
            final InetAddress serverAddress = InetAddress.getByName(ImProtocol.SERVER_IP);
            super.connect(new InetSocketAddress(serverAddress, ImProtocol.SERVER_PORT), Const.SERVER_CONNECT_TIME_OUT);
        } catch (Exception e) {
            mCallback.sendEmptyMessage(Const.UM_NETWORK_ERROR);
            return;
        }

        try {
            // 创建数据发送者
            mSender = new ObjectOutputStream(super.getOutputStream());

            // 创建数据接收者
            receiver = new ObjectInputStream(super.getInputStream());

            // 循环接收数据，直到自己被关闭
            while (!super.isClosed()) {
                // 接收数据并转换成协议
                final ImProtocol pro = (ImProtocol)receiver.readObject();

                // 处理协议
                this.onGetMessageFromServer(pro);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 发送数据。
     * @param pro   协议
     * @return 发送数据的线程
     */
    private Thread sendData(ImProtocol pro) {
        final Thread thread = new SendThread(pro);
        thread.start();

        // 防止发送过快导致服务器无法响应
        try {
            Thread.sleep(100);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return thread;
    }
}
